# -*- coding:utf-8 -*-
# 导入库
from osgeo import gdal
import numpy as np
from PyQt5 import QtWidgets
import re
import traceback
import pandas as pd
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import Qt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtGui import QImage
import matplotlib.pyplot as plt
from matplotlib.pylab import mpl
from PyQt5.QtGui import QWheelEvent
import math

mpl.rcParams['font.sans-serif'] = ['SimHei']  # 中文显示
mpl.rcParams['axes.unicode_minus'] = False  # 负号显示
#待解决问题： 根据序号找对应航线名 将绘图板块独立封装 自动识别最小点距 自动加点  修改后刷新画布


# 坐标转行列公式
# column = int((x – originX) / pixelWidth) + 1
# line = int((y – originY) / pixelHeight) + 1

# 行列坐标转空间坐标（栅格中心点空间坐标）的计算公式如下：
# x = columnpixelWidth + originX – pixelWidth/2
# y = linepixelHeight + originY – pixelHeight/2

#一些方便计算的函数
#据提取处理数据函数
def calculate_distance(point1, point2):
    x1, y1 = point1
    x2, y2 = point2
    return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
def calculate_point(self, x, y, newdist, mode):
    if (x >= self.data_processor.left) & (x <= self.data_processor.right):
        if (y >= self.data_processor.bottom) & (y <= self.data_processor.top):
            col = int((x - self.data_processor.left) / self.data_processor.pixelWidth)
            row = int((y - self.data_processor.top) / self.data_processor.pixelHeight)
            elevation = round(self.data_processor.elevation_data[row, col], 2)
            if mode == 'bj':
                self.ax.annotate(str(elevation), xy=(newdist, elevation), xytext=(-15, -5),
                                 textcoords='offset points',
                                 arrowprops=dict(arrowstyle='-', color='none'),
                                 fontsize=14, color='black', weight='bold')
            self.point.append([x, y, newdist, elevation])
def Grid_Pointing(self, Dot_Pitch, mode):
    if mode == 'ys':
        self.mark = self.high_density_point[self.dmnum + 1 ]
    if mode == 'bj':
        self.mark = self.dmnum_point[self.dmnum + 1]

    #高密度点和fix点要分开存储,用mark标记
    if self.mark.empty is True:
        lines = self.data_processor.lines
        target_route = self.dmnum + 1
        newdist = 0  # 距离步进
        x = lines[target_route][0][0]  # x方向步进
        y = lines[target_route][0][1]  # y方向步进
        Dot_Pitch = Dot_Pitch  # 指定点距
        self.point = []

        # 第一个数
        calculate_point(self, lines[target_route][0][0], lines[target_route][0][1], 0, mode)

        # 利用方位角的三件函数运算，快速确定X,Y步进值
        starting_point = (lines[target_route][0][0], lines[target_route][0][1])
        ending_point = (lines[target_route][1][0], lines[target_route][1][1])
        distance = round(calculate_distance(starting_point, ending_point), 2)
        # 中间的数
        sin = (lines[target_route][1][0] - lines[target_route][0][0]) / distance
        cos = (lines[target_route][1][1] - lines[target_route][0][1]) / distance
        while newdist < distance:
            x = round(x + Dot_Pitch * sin, 2)
            y = round(y + Dot_Pitch * cos, 2)
            newdist += Dot_Pitch
            calculate_point(self, x, y, newdist, mode)
        # 最后一个数
        calculate_point(self, lines[target_route][1][0], lines[target_route][1][1], distance, mode)

        # 在self.point里洗掉高程为nan的数据
        self.point = [t for t in self.point if not math.isnan(t[3])]

        if self.point != []:
            self.mark = pd.DataFrame(self.point)  #列表转为dataframe
            self.mark.columns = ['X', 'Y', '距离', '高程']

            if mode == 'ys':
                self.high_density_point[self.dmnum+1] = self.mark

            if mode == 'bj':
                self.dmnum_point[self.dmnum + 1] = self.mark
                self.draw_point[self.dmnum + 1] = self.dmnum_point[self.dmnum + 1]




    self.fig.canvas.draw_idle()

#数据处理类
class DataProcessor:
    def __init__(self,window):
        self.elevation_data = None #在这里是高程数据
        self.geo_transform = None
        self.band = None
        self.lines = {}    #航线
        self.window =window
        self.X =None
        self.Y =None
        self.left = None
        self.right = None
        self.bottom = None
        self.top = None
        self.pixelWidth = None
        self.pixelHeight = None
        self.range_control = None    #控制坐标系是否变化
        self.dmnum_point = {}   #存储原断面点
        self.high_density_point = {} #存储高精度栅格点
        self.ADD = {} #存储新增点
        self.delete = {} #存储删除点
        self.draw_point = {} #存储绘图点
    def import_rasterdata(self):
        try:
            # 打开文件对话框，让用户选择文件
            filter = 'ASC or TIFF Files (*.asc *.tif *.tiff)'
            file_path, _ = QtWidgets.QFileDialog.getOpenFileName(window, '打开文件', r'C:\Users\92573\Desktop', filter)
            # 如果用户选择了文件，则输出文件路径。如果用户取消选择，则输出空字符串。
            if file_path:
                # 读取GeoTIFF文件
                dataset = gdal.Open(file_path)
                if dataset is None:
                    print("无法打开GeoTIFF文件")
                else:
                    # 获取地理转换信息，包括左上角x坐标，x方向分辨率，0，左上角y坐标，0，y方向分辨率
                    print(dataset.GetGeoTransform())
                    self.geo_transform = dataset.GetGeoTransform()
                    # 计算图像的宽度和高度
                    width = dataset.RasterXSize
                    height = dataset.RasterYSize

                    originX = self.geo_transform[0]
                    originY = self.geo_transform[3]
                    self.pixelWidth = self.geo_transform[1]
                    self.pixelHeight = self.geo_transform[5]  #负值

                    # 计算地理坐标范围
                    self.left = originX
                    self.top = originY
                    self.right = self.left + width * self.geo_transform[1]
                    self.bottom = self.top + height * self.geo_transform[5]


                    # 获取高程数据
                    self.band = dataset.GetRasterBand(1)
                    self.elevation_data = self.band.ReadAsArray()

                    # 获取无效值的标识
                    nodata_value = self.band.GetNoDataValue()

                    # 如果有无效值，则用np.nan替换掉
                    if nodata_value is not None:
                        self.elevation_data = np.where(self.elevation_data == nodata_value, np.nan, self.elevation_data)
                    self.range_control = 0 #坐标系呈初始状态
                    self.window.label.setText(f'{file_path}加载成功')
                    # 加载到俯视图
                    self.window.canvas.set_data_processor(self)
                    self.window.canvas.update_canvas()

        except Exception as e:
            # 发生异常时，打印异常信息和出错行，并设置 label3 的文本为加载失败
            error_msg = f"导入栅格数据发生错误：{str(e)}"
            traceback_msg = traceback.format_exc()  # 获取完整的错误追踪信息
            print(f"{error_msg}\n{traceback_msg}")
            self.window.label.setText(f"加载失败\n错误信息：{error_msg}\n出错行：{traceback_msg}")
    def import_shiproute(self):
        # 手动导入航线并存进字典
        line_path, _ = QtWidgets.QFileDialog.getOpenFileName(window, '打开文件', '', '*.Lnw')
        print(line_path)
        try:
            if line_path:
                # 定义正则匹配航线
                pts_regex = re.compile(r'\bPTS\s+(\d+(\.\d+)?)\s+(\d+(\.\d+)?)\b')
                lnn_regex = re.compile(r'\bLNN\s*(\d+)\b')
                eol_regex = re.compile(r'EOL')
                with open(line_path, 'r') as f:
                    curr_lnn = None  # 跟踪当前组的辅助变量
                    curr_pts = []  # 创建用于存储当前组 PTS 数据的列表
                    for line in f:
                        #print(line)
                        pts_match = pts_regex.search(line)
                        if pts_match:  # 如果匹配到 PTS，则提取两个数字，并加入 curr_pts 列表
                            curr_pts.append((float(pts_match.group(1)), float(pts_match.group(3))))
                            print(curr_pts)
                        lnn_match = lnn_regex.search(line)
                        print(lnn_match)
                        if lnn_match:  # 如果匹配到 LNN，则认为当前组已经处理完毕，将 curr_pts 存储到字典中
                            if curr_lnn is not None:
                                self.lines[curr_lnn] = curr_pts
                                curr_pts = []  # 重置 curr_pts 以存储后续组的数据
                            curr_lnn = int(lnn_match.group(1))  # 记录当前组的 LNN

                        if eol_regex.search(line) and curr_lnn is not None:  # 如果匹配到 EOL，则认为前面的所有 PTS 已处理完毕，将其存储到字典中
                            self.lines[curr_lnn] = curr_pts
                            curr_pts = []  # 重置 curr_pts 以存储后续组的数据
                            curr_lnn = None  # 重置 curr_lnn 以存储后续组的数据
                print('总航线字典')
                print(self.lines)

                #字典初始化为空dataframe
                for i in range(len(self.lines)):
                    self.ADD[i+1] = pd.DataFrame()
                    self.dmnum_point[i+1] = pd.DataFrame()
                    self.high_density_point[i+1] = pd.DataFrame()
                    self.delete[i+1] = pd.DataFrame()
                    self.draw_point[i+1] = pd.DataFrame()

                self.range_control = 0  # 坐标系呈初始状态
                self.window.label.setText(f'{line_path}加载成功')
                # 加载到俯视图
                self.window.canvas.set_data_processor(self)
                self.window.canvas.update_canvas()
                # 加载到断面图
                self.window.cross_section.set_data_processor(self)
                self.window.cross_section.update_canvas()
        except Exception as e:
            # 发生异常时，打印异常信息和出错行，并设置 label3 的文本为加载失败
            error_msg = f"导入栅格数据发生错误：{str(e)}"
            traceback_msg = traceback.format_exc()  # 获取完整的错误追踪信息
            print(f"{error_msg}\n{traceback_msg}")
            self.window.label.setText(f"加载失败\n错误信息：{error_msg}\n出错行：{traceback_msg}")

#基类
class BaseWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMouseTracking(True)  # 启用鼠标跟踪
        self.setFocusPolicy(Qt.ClickFocus)
        self.setStyleSheet("background-color: white;")  # 设置画布背景色为白色
        self.data_processor = None
        self.draw_enabled = False  # 标记是否启用绘制
        self.canvas =None #存储画布控件
        self.fig = None  # 存储fig和ax作为实例变量
        self.ax = None
        self.dragging = None
        self.scale_factor = 1.0  # 缩放因子的初始值为1.0
        self.pos =None   #光标位置
        self.create_figure()  # 创建绘图对象和子图
        #保证刷新绘图界面位置不变
        self.new_xstart = None
        self.new_xend = None
        self.new_ystart = None
        self.new_yend = None
        #控制增删改查开关
        self.add = False
        self.point_del = False
        self.change = False
        self.sql = False

    def create_figure(self):
        # 创建绘图对象和子图
        self.fig = plt.Figure(figsize=(25, 25), dpi=100)
        self.ax = self.fig.add_subplot(111)
        self.ax.axis("off")  #隐藏坐标轴和边框
        # 创建画布并在当前窗口上显示
        self.canvas = FigureCanvas(self.fig)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.canvas)
        #连接matplotlib鼠标处理事件
        self.fig.canvas.mpl_connect("button_press_event", self.on_press)
        self.fig.canvas.mpl_connect("button_release_event", self.on_release)
        self.fig.canvas.mpl_connect("motion_notify_event", self.on_move)
        self.fig.canvas.mpl_connect('scroll_event', self.on_scroll)

    def paintEvent(self, event):
        if self.draw_enabled:
            self.update_canvas()
    def on_scroll(self, event: QWheelEvent):
        if self.data_processor is not None:

            if event.inaxes == self.ax:
                if event.button == 'up':
                    self.scale_factor = 0.8  # 缩小比例因子
                elif event.button == 'down':
                    self.scale_factor = 1.2  # 放大比例因子
                else:
                    self.scale_factor = 1.0

                # 获取鼠标点击的坐标位置
                x_mouse = event.xdata
                y_mouse = event.ydata

                # 获取当前坐标轴的范围
                xstart, xend = self.ax.get_xlim()
                ystart, yend = self.ax.get_ylim()

                # 计算当前范围中心点坐标
                xcenter = (xstart + xend) / 2
                ycenter = (ystart + yend) / 2

                # 计算缩放后的新范围
                new_xrange = (xend - xstart) * self.scale_factor
                new_yrange = (yend - ystart) * self.scale_factor

                # 计算新的中心点坐标
                new_xcenter = x_mouse - (x_mouse - xcenter) * self.scale_factor
                new_ycenter = y_mouse - (y_mouse - ycenter) * self.scale_factor

                # 计算新的范围
                self.new_xstart = new_xcenter - new_xrange / 2
                self.new_xend = new_xcenter + new_xrange / 2
                self.new_ystart = new_ycenter - new_yrange / 2
                self.new_yend = new_ycenter + new_yrange / 2
                #标记坐标系已经改变
                self.data_processor.range_control = 1
                self.ax.set_xlim(self.new_xstart, self.new_xend)   # 更新坐标轴范围
                self.ax.set_ylim(self.new_ystart, self.new_yend)
                self.ax.figure.canvas.draw_idle()   # 更新画布显示
    def on_press(self, event):
        if event.inaxes ==self.ax :  # 判断鼠标是否在axes内
            if event.button == 2 or event.button == 3: # 判断按下的是否为鼠标滚轮键、或者右键，防止有人滚轮键不灵
                self.dragging = True  # 标记开始拖拽
                self.lastx = event.xdata  # 获取鼠标按下时的坐标X
                self.lasty = event.ydata  # 获取鼠标按下时的坐标Y
    def on_move(self, event):
        if self.data_processor is not None:
            if self.dragging :  # 当处于拖拽状态时
                if (event.inaxes == self.ax) & (self.dragging == 1):
                    # 计算新的坐标原点并移动
                    # 获取当前最新鼠标坐标与按下时坐标的差值
                    dx = event.xdata - self.lastx
                    dy = event.ydata - self.lasty

                    # 获取当前坐标轴的范围
                    xstart, xend = self.ax.get_xlim()
                    ystart, yend = self.ax.get_ylim()
                    self.new_xstart = xstart - dx
                    self.new_xend = xend - dx
                    self.new_ystart = ystart - dy
                    self.new_yend = yend - dy
                    self.data_processor.range_control = 1  # 表示范围被改动，下次画图按改动后范围画图
                    self.ax.set_xlim(self.new_xstart, self.new_xend)
                    self.ax.set_ylim(self.new_ystart, self.new_yend)
                    self.ax.figure.canvas.draw()  # 更新画布
    def on_release(self, event):
        if self.dragging == True:  # 判断是否释放鼠标左键
            self.dragging = False  # 标记停止拖拽
    def control_add(self):
        self.add = True
        print('add')
#栅格数据提取线数据点内插
#新思路，直接从线上先取点，在求该点在栅格数的行列号，然后根据行列号定位，按自己想法内插！
# def world2Pixel(geotransform, x, y):
#     originX = geotransform[0]
#     originY = geotransform[3]
#     pixelWidth = geotransform[1]
#     pixelHeight = geotransform[5]
#     line = int((y-originY)/pixelHeight)+1
#     column = int((x-originX)/pixelWidth)+1
#     return (line,column)

#封装控制点的字典
class MyDict:
    def __init__(self, initial_dict=None):
        self.dict = initial_dict if initial_dict else {}

    def add(self, key, value):
        self.dict[key] = value

    def remove(self, key):
        if key in self.dict:
            del self.dict[key]

    def get(self, key):
        return self.dict.get(key)

    def keys(self):
        return self.dict.keys()

    def values(self):
        return self.dict.values()

    def items(self):
        return self.dict.items()

    def clear(self):
        self.dict.clear()


#俯视图
class CanvasWidget(BaseWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.data_processor = None
        self.line_objects = None  # 创建存储绘图对象的列表,用来存放每条航线
        self.dmnum = 0     #标记断面号

    def set_data_processor(self, data_processor):
        self.data_processor = data_processor
    def update_canvas(self):
        if self.data_processor is not None :
            elevation_data = self.data_processor.elevation_data

            self.ax.clear()  # 清屏幕

            if elevation_data is not None:
                # 绘制高程分布
                self.ax.imshow(
                    elevation_data,
                    cmap='gray',
                    extent=[
                        self.data_processor.left,
                        self.data_processor.right,
                        self.data_processor.bottom,
                        self.data_processor.top
                    ]
                )

            if self.data_processor.lines != {}:
                # 绘制航线分布
                # 绘制初始航线
                self.line_objects = []
                for route, points in self.data_processor.lines.items():
                    x_coords, y_coords = zip(*points)
                    line, = self.ax.plot(x_coords, y_coords, color='green', label=f'Route {route}')
                    self.line_objects.append(line)  # 将绘图对象添加到列表中

                target_route = self.dmnum + 1
                color = 'red'
                self.modify_line_color(target_route, color)
            self.canvas.draw()
    def modify_line_color(self, target_route, color):
        if 1 <= target_route <= len(self.line_objects):
            # 查找目标航线对应的绘图对象
            target_line = self.line_objects[target_route - 1]

            # 修改目标航线的颜色为指定颜色
            target_line.set_color(color)
            # 更新绘图
            if self.data_processor.range_control == 1:
                self.ax.set_xlim(self.new_xstart, self.new_xend)
                self.ax.set_ylim(self.new_ystart, self.new_yend)
            self.ax.figure.canvas.draw_idle()
    def switch_previous_section(self):
        if self.line_objects:
            if self.dmnum >1:
                self.modify_line_color(self.dmnum,color='green')
                self.dmnum -=1
                self.modify_line_color(self.dmnum, color='red')


    def switch_next_section(self):
        if self.line_objects:
            if self.dmnum < len(self.line_objects):

                self.modify_line_color(self.dmnum, color='green')
                self.dmnum += 1
                self.modify_line_color(self.dmnum, color='red')

#断面图
class CrossSectionWidget(BaseWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.data_processor = None
        self.line_objects = None  # 创建存储绘图对象的列表,用来存放每条航线
        self.dmnum = 0
        # 连接matplotlib鼠标处理事件
        self.fig.canvas.mpl_connect("button_press_event", self.on_add)
        self.dmnum_point = None
        self.high_density_point = None
        self.ADD = None
        self.delete = None
        self.draw_point = None

    def set_data_processor(self, data_processor):
        self.data_processor = data_processor
        self.dmnum_point = self.data_processor.dmnum_point
        self.high_density_point = self.data_processor.high_density_point
        self.ADD = self.data_processor.ADD
        self.delete = self.data_processor.delete
        self.draw_point = self.data_processor.draw_point

    def plot_data(self):
        # 绘图部分
        self.ax.cla()
        # 添加x轴和y轴标签
        self.ax.set_xlabel('距离')
        self.ax.set_ylabel('水深')

        # 添加标题
        self.ax.set_title('第%s条线' % [self.dmnum + 1])
        try:
            # 绘底图
            if self.high_density_point[self.dmnum + 1].empty is False:
                self.ax.plot(self.high_density_point[self.dmnum + 1]['距离'], self.high_density_point[self.dmnum + 1]['高程'],
                             color='black', linewidth=0.6,
                             linestyle='-')  # 画线，EC1[dmnum]
            # 绘目标折线
            self.ax.plot(self.draw_point[self.dmnum + 1]['距离'], self.draw_point[self.dmnum + 1]['高程'],
                         color='lime', linewidth=0.7,
                         linestyle='-')  # 画线，EC1[dmnum]

            yz_min, yz_max = self.ax.get_ylim()
            if self.ADD[self.dmnum + 1].empty is False:
                # 标记加点垂线
                self.ax.vlines(self.ADD[self.dmnum + 1]['距离'], yz_min, yz_max, linestyles='-', colors='blue',
                               linewidth=0.7)
            # 标记初始垂线
            self.ax.vlines(self.dmnum_point[self.dmnum + 1]['距离'], yz_min, yz_max, linestyles='-', colors='red',
                           linewidth=0.7)
            # 贴高程标签
            for x, z in zip(self.draw_point[self.dmnum + 1]['距离'], self.draw_point[self.dmnum + 1]['高程']):
                self.ax.annotate(round(z, 2), xy=(x, z), xytext=(-15, -5),
                                 textcoords='offset points',
                                 arrowprops=dict(arrowstyle='-', color='none'),
                                 fontsize=12, color='black', weight='bold')


        except:
            pass
    def update_canvas(self):
        if self.data_processor is not None:
            if self.data_processor.lines != {}:
                self.ax.clear()  # 清屏幕

                Grid_Pointing(self,2,'ys')   #最小单元，第二个参数为点距，后续设置为自动识别最小单元。
                Grid_Pointing(self,20,'bj')  #fix点
                self.plot_data()
            #应该把绘制图像和数据处理彻底分开，设置一个原点集合，一个新增点集合，一个总点集合。前两者用来绘制垂线，区分原点和新增点，后者用来绘制额折线图。

    def on_add(self,event):

        if event.inaxes == self.ax:
            if event.button == 1 & self.add == True:
                xadd = round(event.xdata, 2)
                # # 寻找据起点距离 - xadd差值最小处的索引
                # idx = (self.dmnum_point[self.dmnum+1]['距离'] - xadd).abs().idxmin()
                # # 取该点的离原点距离，高程
                # yadd = self.dmnum_point[self.dmnum+1].loc[idx, '高程']

                # 找目的最近两个横坐标
                closest_indices = (self.dmnum_point[self.dmnum+1]['距离'] - xadd).abs().nsmallest(2).index
                #print(closest_indices)
                #思路根据距离，内插坐标，高程。
                xnew = round(np.interp(xadd,self.dmnum_point[self.dmnum + 1]['距离'],
                                       self.dmnum_point[self.dmnum + 1]['X']),2)
                ynew = round(np.interp(xadd,self.dmnum_point[self.dmnum + 1]['距离'],
                                       self.dmnum_point[self.dmnum + 1]['Y']),2)
                znew = round(np.interp(xadd,self.high_density_point[self.dmnum + 1]['距离'],
                                       self.high_density_point[self.dmnum + 1]['高程']),2)
                result = [xnew,ynew,xadd,znew]


                if self.ADD[self.dmnum+1].empty is True:
                    self.ADD[self.dmnum + 1] = pd.DataFrame(result)
                    self.ADD[self.dmnum + 1] = self.ADD[self.dmnum + 1].T
                    self.ADD[self.dmnum + 1].columns = ['X', 'Y', '距离', '高程']
                else:
                    temp = pd.DataFrame(result)
                    temp = temp.T
                    temp.columns = ['X', 'Y', '距离', '高程']
                    self.ADD[self.dmnum + 1] = pd.concat([self.ADD[self.dmnum + 1], temp], ignore_index=True)


                self.draw_point[self.dmnum+1] = pd.concat([self.dmnum_point[self.dmnum + 1],self.ADD[self.dmnum+1]],ignore_index=True)

                self.draw_point[self.dmnum+1] = self.draw_point[self.dmnum+1].sort_values(by=['距离'],
                                                ascending=True)   # 需要降序就加上ascending=False
                self.draw_point[self.dmnum+1] = self.draw_point[self.dmnum+1].reset_index(drop=True)  # 重新排序
                #print(self.ADD[self.dmnum+1])
                #print(self.draw_point[self.dmnum+1])
                self.add = False  #关闭按钮
                self.plot_data()

                if self.data_processor.range_control == 0:
                    self.new_xstart, self.new_xend = event.inaxes.get_xlim()
                    self.new_ystart, self.new_yend = event.inaxes.get_ylim()
                self.ax.set_xlim(self.new_xstart, self.new_xend)
                self.ax.set_ylim(self.new_ystart, self.new_yend)
                self.ax.figure.canvas.draw()  # 更新画布
    def switch_previous_section(self):

        if self.data_processor.lines != {}:
            if self.dmnum >1:
                self.dmnum -=1
                self.update_canvas()
    def switch_next_section(self):

        if self.data_processor.lines != {}:
            if self.dmnum < len(self.data_processor.lines):
                self.dmnum += 1
                self.update_canvas()

#主界面
class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()  # 调用父类构造函数，初始化 QMainWidow 的一些属性和功能
        self.initUI()
    def initUI(self):
        #设置窗口标题、大小等
        self.setWindowTitle('皖江绘水下篇——安徽省长江河道管理局')
        self.setGeometry(450, 150, 720, 480)
        self.showMaximized()  # 最大化显示窗口

        # 创建整体布局
        main_layout = QtWidgets.QVBoxLayout()

        #创建菜单栏
        self.dataprocessor = DataProcessor(self)  # 建立一个实例用来操控类

        menubar = self.menuBar()  # 创建菜单栏
        file_menu = menubar.addMenu('文件')  # 添加一个名为 "文件" 的菜单

        import_action = QtWidgets.QAction('读取栅格数据', self)  # 创建一个动作项 "读取栅格数据"
        import_action.triggered.connect(self.dataprocessor.import_rasterdata)
        file_menu.addAction(import_action)

        import_action = QtWidgets.QAction('导入航线', self)  # 创建一个动作项 "导入航线"
        import_action.triggered.connect(self.dataprocessor.import_shiproute)
        file_menu.addAction(import_action)

        # 创建菜单下方的 QLabel 栏
        funct_layout = QtWidgets.QHBoxLayout()
        self.label = QtWidgets.QLabel("你好")
        self.label.setAlignment(Qt.AlignCenter)  #放在中间
        self.label.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        funct_layout.addWidget(self.label)
        #创建菜单下方的切换断面按钮
        funct_layout.addStretch()  # 添加一个弹簧

        prev_button = QtWidgets.QPushButton('上一条', self)  # 创建一个名为 "上一条" 的按钮
        prev_button.setFixedSize(QtCore.QSize(100, 30))  # 设置按钮的宽度和高度
        funct_layout.addWidget(prev_button)

        next_button = QtWidgets.QPushButton('下一条', self)  # 创建一个名为 "下一条" 的按钮
        next_button.setFixedSize(QtCore.QSize(100, 30))  # 设置按钮的宽度和高度
        funct_layout.addWidget(next_button)

        add_button = QtWidgets.QPushButton('加点', self)  # 创建一个名为 "加点" 的按钮
        add_button.setFixedSize(QtCore.QSize(100, 30))  # 设置按钮的宽度和高度
        add_button.setShortcut('Alt+A')                #设置快捷键Alt+A
        funct_layout.addWidget(add_button)

        del_button = QtWidgets.QPushButton('删点', self)  # 创建一个名为 "删点" 的按钮
        del_button.setFixedSize(QtCore.QSize(100, 30))  # 设置按钮的宽度和高度
        funct_layout.addWidget(del_button)

        cha_button = QtWidgets.QPushButton('改点', self)  # 创建一个名为 "删点" 的按钮
        cha_button.setFixedSize(QtCore.QSize(100, 30))  # 设置按钮的宽度和高度
        funct_layout.addWidget(cha_button)

        inquire_button = QtWidgets.QPushButton('查询', self)  # 创建一个名为 "删点" 的按钮
        inquire_button.setFixedSize(QtCore.QSize(100, 30))  # 设置按钮的宽度和高度
        funct_layout.addWidget(inquire_button)



        funct_layout.addStretch()  # 添加一个弹簧

        # 创建左右分布的画布
        canvas_layout = QtWidgets.QHBoxLayout()
        self.canvas = CanvasWidget()  # 创建画布小部件
        self.cross_section = CrossSectionWidget()  # 创建断面图小部件
        canvas_layout .addWidget(self.canvas)  # 将画布小部件添加到横向布局中
        canvas_layout .addWidget(self.cross_section)  # 将断面图小部件添加到横向布局中
        #关联一些命令到画布控件上_俯视图
        prev_button.clicked.connect(self.canvas.switch_previous_section)
        next_button.clicked.connect(self.canvas.switch_next_section)
        #关联一些命令到画布控件上_断面图
        prev_button.clicked.connect(self.cross_section.switch_previous_section)
        next_button.clicked.connect(self.cross_section.switch_next_section)
        add_button.clicked.connect(self.cross_section.control_add)
        # 将菜单、QLabel 和画布加入主布局
        main_layout.addLayout(funct_layout)
        main_layout.addLayout(canvas_layout)

        # 创建一个 QWidget 并设置主布局
        widget = QtWidgets.QWidget()
        widget.setLayout(main_layout)

        # 将该 QWidget 设置为主窗口的中心部件
        self.setCentralWidget(widget)
        self.show()

if __name__ == '__main__':

    app = QtWidgets.QApplication([])  # 创建 QApplication 实例
    window = MyWindow()  # 创建自定义窗口实例
    app.exec_()  # 运行应用程序主循环



